﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace org.Alexander.EDAS
{
    public class Acceptor
    {
        private long counter;
        private Socket acceptSock;
        private IPEndPoint local;
        private AutoResetEvent acceptlock;

        public int BufferSize
        {
            get;
            set;
        }

        public int BlockSize
        {
            get;
            set;
        }

        public bool Working
        {
            get;
            set;
        }

        public Action<Socket, long, int> Accepted;
        public Action<Exception, string> OnError;

        public Acceptor(int port) : this(new IPEndPoint(IPAddress.Any, port)) { }
        public Acceptor(IPEndPoint localhost) : this(localhost, 4096) { }
        public Acceptor(IPEndPoint localhost, int buffersize) : this(localhost, buffersize, 200) { }
        public Acceptor(IPEndPoint localhost, int buffersize,int blocksize)
        {
            local = localhost;
            BufferSize = buffersize;
            BlockSize = blocksize;
            Initacceptor(local);
        }

        private void Initacceptor(IPEndPoint localhost)
        {
            acceptSock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
            acceptSock.Bind(localhost);
            acceptlock = new AutoResetEvent(true);
        }

        public void WaitAccept()
        {
            Socket AcceptedSocket;
            acceptSock.Listen(BlockSize);
            Working = true;
            while (Working)
            {
                try
                {
                    AcceptedSocket = acceptSock.Accept();
                    counter++;
                    if (Accepted != null)
                    {
                        Accepted(AcceptedSocket, counter, BufferSize);
                    }
                }
                catch (Exception ex)
                {
                    RaisError(ex, "accept exception");
                }
            }
        }

        public void WaitAsyncAccept()
        {
            acceptSock.Listen(BlockSize);
            Working = true;
            AcceptAsync();
        }

        private void AcceptAsync()
        {
            SocketAsyncEventArgs arg = new SocketAsyncEventArgs();
            arg.Completed += new EventHandler<SocketAsyncEventArgs>(arg_Completed);
            bool RaisEvent = false;
            try
            {
                RaisEvent = acceptSock.AcceptAsync(arg);
            }
            catch (Exception ex)
            {
                RaisError(ex, "accept error");
                Close();
            }
            if (!RaisEvent)
            {
                ProcessAccept(acceptSock, arg);
            }
        }

        void arg_Completed(object sender, SocketAsyncEventArgs e)
        {
            //throw new NotImplementedException();
            acceptlock.WaitOne();
            ProcessAccept(sender, e);
            acceptlock.Set();
        }

        private void ProcessAccept(object sender, SocketAsyncEventArgs e)
        {
            try
            {
                using (e)
                {
                    if (Working)
                    {
                        if (e.SocketError == SocketError.Success)
                        {
                            try
                            {
                                Accepted(e.AcceptSocket, counter, BufferSize);
                                counter++;
                            }
                            catch (Exception ex)
                            {
                                RaisError(ex, "user program error");
                            }
                        }
                        else
                        {
                            RaisError(null, "socket error:" + e.SocketError.ToString());
                        }
                        AcceptAsync();
                    }
                    else
                    {
                        Stop();
                    }
                }
            }
            catch (Exception ex)
            {
                Stop();
                RaisError(ex, "normal excption");
            }
        }

        public void Stop()
        {
            Working = false;
            acceptSock.Close();
            Initacceptor(local);
        }
        public void Close()
        {
            Working = false;
            acceptSock.Close();
        }

        private void RaisError(Exception ex,string Message)
        {
            try
            {
                if (OnError != null)
                {
                    OnError(ex, Message);
                }
            }
            catch { }
        }

    }
}
